(function(window) {
    var Util = window.Util;
    var Grid = window.mbg.MBG;
    var ComBox = window.ComBox;

    var Main = function() {
        this.init();
    };

    Main.prototype = {
        init: function() {
            this.initMain();
            this.initSocket();
            this.initDragger();
            this.initTabs();
            this.initTooltip();
            this.initProject();
            this.initGrid();
        },

        initMain: function() {
            this.faviconIndex = 0;
            this.favicon = document.querySelector('link[rel="icon"]');
            this.previousFavicon = this.favicon.getAttribute("href");
            this.canvas = document.createElement("canvas");
            //document.body.appendChild(canvas);
            this.canvas.width = 16;
            this.canvas.height = 16;
            this.ctx = this.canvas.getContext("2d");
            this.$content = $(".gui-output-content");
        },

        initSocket: function() {
            this.socket_list = [];
            var self = this;
            this.socket = window.io.connect("/");
            this.socket.on("data", function(data) {
                self.socketDataHandler(data);
            });
            this.socket.on("connect", function(data) {
                self.showStatus("Socket Connected");
                self.requestData({
                    name: "getProjectInfo"
                });
            });

            this.socket.on("connect_error", function(data) {
                self.showError("Socket Connect error");
            });

            this.socket.on("connect_timeout", function(data) {
                self.showError("Socket Connect timeout");
            });

            this.socket.on("reconnecting", function(data) {
                self.showStatus("Socket Reconnecting ...");
            });

            this.socket.on("reconnect_error", function(data) {
                self.showError("Socket Reconnect error");
            });

            this.socket.on("reconnect_failed", function(data) {
                self.showError("Socket Reconnect failed");
            });
        },

        sendSocketList: function() {
            if (!this.socket_list.length) {
                return;
            }
            var self = this;
            clearTimeout(this.timeout_socket_list);
            this.timeout_socket_list = setTimeout(function() {
                self.sendSocketItem();
            }, 0);

        },

        sendSocketItem: function() {
            var item = this.socket_list.shift();
            if (item && this.socket) {
                this.socket.emit("data", item);
            } else {
                console.log("Invalid socket");
            }
            this.sendSocketList();
        },

        //===================================================================================

        sendMessage: function(message) {
            if (!message) {
                return;
            }
            this.socket_list.push({
                type: "message",
                data: message
            });
            this.sendSocketList();
        },

        sendCmd: function(cmd) {
            if (!cmd) {
                return;
            }
            this.socket_list.push({
                type: "cmd",
                data: cmd
            });
            this.sendSocketList();
        },

        requestData: function(data) {
            if (!data) {
                return;
            }
            this.socket_list.push({
                type: "data",
                data: data
            });
            this.sendSocketList();
        },

        requestDataSync: function(data) {
            if (this.socket) {
                this.socket.emit("data", {
                    type: "data",
                    data: data
                });
            }
        },

        //===================================================================================

        socketDataHandler: function(data) {
            if (!data) {
                return;
            }

            var handlers = {
                //only for text message
                message: this.messageHandler,
                //only for bd cmd
                cmd: this.cmdHandler,
                //for data function 
                data: this.dataHandler
            };

            var handler = handlers[data.type];
            if (handler) {
                handler.call(this, data.data);
            }
        },

        dataHandler: function(data) {
            if (!data || !data.name) {
                console.log("invalid data arguments");
                return;
            }
            var handler = this[data.name];
            if (typeof(handler) === "function") {
                // console.log("data handler: " + data.name);
                handler.call(this, data);
            } else {
                var msg = "Not found handler: " + data.name;
                console.log(msg);
                this.showError(msg);
            }
        },

        //===================================================================================
        draggerHandler: function(dragger) {
            var d = dragger.startH + (dragger.startY - dragger.posY);
            dragger.target.height(d + "px");
            var self = this;
            this.throttle(100, function() {
                self.resize();
                self.scrollTo(true);
            });

        },

        initDragger: function() {
            var $target = $(".gui-body");
            var dragger = {
                target: $target
            };
            var self = this;
            var $dragger = $(".gui-dragger").unbind();
            $dragger.bind("mousedown.gui-dragger", function(e) {
                e.preventDefault();
                var global = $(window);
                $(document.body).addClass("gui-dragger-cursor");
                global.bind("mousemove.gui-dragger", function(e) {
                    //console.log(e.type);
                    e.preventDefault();
                    dragger.posY = e.pageY;
                    self.draggerHandler(dragger);
                });
                global.bind("mouseup.gui-dragger", function(e) {
                    //console.log(e.type);
                    global.unbind("mouseup.gui-dragger");
                    global.unbind("mousemove.gui-dragger");
                    $(document.body).removeClass("gui-dragger-cursor");
                    dragger.posY = e.pageY;
                    self.draggerHandler(dragger);
                });
                //console.log(e.type);
                dragger.startY = e.pageY;
                dragger.startH = $target.height();
            });
        },

        //===================================================================================

        initTabs: function() {
            var self = this;
            $(".gui-tab").delegate(".gui-tab-item", "click", function() {
                var $item = $(this);
                var data = $item.attr("data");
                self.tabChangeHandler(data);
            });
        },

        tabChangeHandler: function(name) {
            if (!name) {
                name = "project";
            }
            this.currentTab = name;
            $(".gui-tab-item").removeClass("selected");
            $(".gui-body-item").removeClass("selected");
            $(".gui-tab-item[data='" + name + "']").addClass("selected");
            $(".gui-body-item[data='" + name + "']").addClass("selected");
            this.resize();
        },

        updateTabs: function(tabs) {

            //console.log(tabs);
            //clean previous custom tabs first
            var $guiTab = $(".gui-tab");
            var $guiBody = $(".gui-body");

            $guiTab.find(".gui-tab-item-custom").remove();
            $guiBody.find(".gui-body-item-custom").remove();

            if (!Util.isList(tabs)) {
                return;
            }

            //append new tabs
            tabs.forEach(function(item, i) {
                if (!item || !item.title || !item.content) {
                    return;
                }
                $("<div/>").addClass("gui-tab-item gui-tab-item-custom")
                    .html(item.title).attr("data", "custom-" + i).appendTo($guiTab);
                $("<div/>").addClass("gui-body-item gui-body-item-custom")
                    .html(item.content).attr("data", "custom-" + i).appendTo($guiBody);
            });
        },

        //===================================================================================

        initTooltip: function() {
            var self = this;
            $(document).delegate("[tooltip]", "mouseenter mouseleave", function(e) {
                self.tooltipRemoveHandler();
                if (e.type === "mouseleave") {
                    return;
                }
                var $target = $(this);
                self.tooltipCreateHandler($target);
            });
        },

        tooltipRemoveHandler: function() {
            if (this.$tooltip) {
                this.$tooltip.remove();
                this.$tooltip = null;
            }
        },

        tooltipCreateHandler: function($target) {
            var tooltip = $target.attr("tooltip");
            if (!tooltip) {
                return;
            }

            var $tooltip = $("<div/>").html(tooltip)
                .addClass("gui-tooltip")
                .appendTo(document.body);

            var tooltipWidth = $tooltip.outerWidth(true);
            var tooltipHeight = $tooltip.outerHeight(true);

            var holderWidth = $(document.body).width();
            //var holderHeight = $(document.body).height();

            var offset = $target.offset();
            var targetTop = offset.top;
            var targetLeft = offset.left;

            var targetWidth = $target.outerWidth(true);
            var targetHeight = $target.outerHeight(true);

            var arrowSize = 5;

            //default is up
            var top = targetTop - tooltipHeight - arrowSize;
            if (top < 0) {
                top = targetTop + targetHeight + arrowSize;
                $tooltip.addClass("gui-tooltip-down");
            }

            //default is center
            var left = targetLeft + targetWidth * 0.5 - tooltipWidth * 0.5;
            if (left < 0) {
                left = 0;
            } else if (left + tooltipWidth > holderWidth) {
                left = holderWidth - tooltipWidth;
            }

            $tooltip.css({
                top: top,
                left: left
            });

            $tooltip.addClass("gui-fade");

            this.$tooltip = $tooltip;
        },

        //===================================================================================

        initProject: function() {
            var self = this;
            $(".gui-project-open").bind("click", function(e) {
                self.openProjectHandler();
            });
            $(".gui-project-restart").bind("click", function(e) {
                self.restartProjectHandler();
            });
        },

        openRemoveHandler: function() {
            if (this.browseGrid) {
                this.browseGrid.destroy();
                this.browseGrid = null;
            }
            if (this.$modal) {
                this.$modal.unbind();
                this.$modal.remove();
                this.$modal = null;
            }
        },

        openProjectHandler: function() {
            this.openRemoveHandler();

            var template = "";
            template += '<div class="gui-modal">';
            template += '<div class="gui-modal-main">';

            template += '<div class="gui-modal-head gui-flex-row">';
            template += '<div class="gui-modal-title gui-flex-auto"></div>';
            template += '<div class="gui-modal-close">x</div>';
            template += "</div>";

            template += '<div class="gui-modal-body"></div>';
            template += '<div class="gui-modal-foot"></div>';
            template += "</div>";
            template += "</div>";

            var $modal = $(template).appendTo(document.body);

            var self = this;
            $modal.bind("click", function(e) {
                var $elem = $(e.target);
                if ($elem.hasClass("gui-modal") || $elem.hasClass("gui-modal-close")) {
                    self.openRemoveHandler();
                }
            });

            this.$modal = $modal;

            if (this.browseGrid) {
                this.browseGrid.showLoading();
            }

            this.browseRequestHandler(this.browsePath);

        },

        restartProjectHandler: function() {
            this.requestData({
                name: "switchProject",
                data: this.info.project
            });
        },

        openDoneHandler: function() {
            this.openRemoveHandler();
            this.requestData({
                name: "switchProject",
                data: this.browsePath
            });
        },

        browseCreateHandler: function() {

            //head
            this.$modal.find(".gui-modal-title").append("Open project");

            //body
            var htmlBody = "";
            htmlBody += '<div class="gui-browse-head gui-flex-row">';
            htmlBody += '<button class="gui-browse-up">Up</button>';
            htmlBody += '<div class="gui-h-space-5"></div>';
            htmlBody += '<input class="gui-input gui-browse-path gui-flex-auto"></input>';
            htmlBody += '<div class="gui-h-space-5"></div>';
            htmlBody += '<button class="gui-browse-go">GO</button>';
            htmlBody += "</div>";
            htmlBody += '<div class="gui-browse-list"></div>';
            this.$modal.find(".gui-modal-body").append(htmlBody);

            //foot
            var htmlFoot = "";
            htmlFoot += '<div class="gui-browse-foot gui-flex-row">';
            htmlFoot += '<input class="gui-input gui-browse-filter" placeholder="Filter"></input>';
            htmlFoot += '<div class="gui-flex-auto"></div>';
            htmlFoot += "<div>Open with a package.json</div>";
            htmlFoot += '<div class="gui-h-space-10"></div>';
            htmlFoot += '<button class="gui-browse-open gui-button-primary disabled">Open</button>';
            htmlFoot += "</div>";
            var $foot = $(htmlFoot);
            this.$modal.find(".gui-modal-foot").append($foot);

            this.browseInitHandler();

        },

        browseInitHandler: function() {
            //create Grid
            var self = this;
            var container = this.$modal.find(".gui-browse-list").get(0);
            this.browseGrid = new Grid(container);
            this.browseGrid.bind("onClick", function(e, d) {

                //console.log("onClick", d);

                var rowData = this.getRowItem(d.row);
                var v = rowData.name;
                if (v === "package.json") {
                    //open
                    self.openDoneHandler();
                    return;
                }

                var browsePath = self.browsePath + "/" + v;
                //console.log("requestData", browsePath);
                self.browseRequestHandler(browsePath);

            });

            this.browseGrid.setOption({
                iconFormat: function(v) {
                    return '<div class="gui-browse-icon gui-browse-icon-' + v + '"></div>';
                },
                rowFilter: function(rowData) {
                    if (!self.browse_keywords) {
                        return true;
                    }
                    var name = rowData.name;
                    if (name === "../" || name === "package.json") {
                        return true;
                    }
                    name = name.toLowerCase();
                    if (name.indexOf(self.browse_keywords) !== -1) {
                        return true;
                    }
                    return false;
                }
            });

            this.$modal.delegate(".gui-browse-up", "click", function(e) {
                var p = self.browsePath + "/../";
                self.browseRequestHandler(p);
            });

            this.$modal.delegate(".gui-browse-go", "click", function(e) {
                var p = self.$modal.find(".gui-browse-path").val();
                if (!p) {
                    return;
                }
                self.browseRequestHandler(p);
            });

            this.$modal.delegate(".gui-browse-open", "click", function(e) {
                var $elem = $(this);
                if ($elem.hasClass("disabled")) {
                    return;
                }
                self.openDoneHandler();
            });

            this.$modal.delegate(".gui-browse-filter", "focus change blur keyup", function(e) {
                var $elem = $(this);
                //console.log(e.type);
                if (e.type === "focusin") {
                    $elem.select();
                    return;
                }

                var k = $elem.val();
                if (k === self.browse_keywords) {
                    return;
                }
                self.browse_keywords = k;

                if (self.browseGrid) {
                    self.browseGrid.update();
                }

            });
        },

        browseRequestHandler: function(browsePath) {
            this.requestData({
                name: "getBrowseInfo",
                data: browsePath
            });

            this.$modal.find(".gui-browse-filter").val("");
            this.browse_keywords = "";
        },

        updateBrowseInfo: function(info) {

            //console.log("updateBrowseInfo", info);

            if (!info || !this.$modal) {
                return;
            }
            //console.log(info);

            this.browsePath = info.browsePath;

            if (!this.browseGrid) {
                this.browseCreateHandler();
            }

            this.$modal.find(".gui-browse-path").val(this.browsePath);

            this.browseGrid.setData(info.gridData);
            this.browseGrid.render();
            this.browseGrid.hideLoading();

            var $open = this.$modal.find(".gui-browse-open");
            if (info.packageJson) {
                $open.removeClass("disabled");
            } else {
                $open.addClass("disabled");
            }

        },

        //===================================================================================

        updateProjectInfo: function(info) {

            this.openRemoveHandler();

            if (!info) {
                this.showError("ERROR: Can NOT get project info");
                return;
            }

            this.info = info;
            console.log(info);

            window.location.hash = info.project;
            this.addHistoryItem("project", info.project + "=" + info.name);
            this.projectHistoryHandler();
            this.tabChangeHandler();
            this.bindEvents();

            this.title = "bd: " + info.name;
            this.showTitleStatus(this.title);

            var branch = info.branch;
            if (info.repository) {
                branch = '<a href="' + info.repository + '" target="_blank">' + info.branch + "</a>";
            }
            $(".gui-project-branch").html(branch);

            $(".gui-project-version").html("v" + info.version);
            $(".gui-bd-version").html(info.bdVersion);
            $(".it-version").val(info.version);

            if (!Util.isList(info.list)) {
                info.list = [];
                this.showError("ERROR: Can NOT get component list");
            }

            this.showList(info.list);

        },

        //=======================================================================================
        //after grid rendered update related data async
        updateProjectRelated: function() {

            //this.requestData({
            //    name: "getReadme"
            //});

            ////load custom tabs
            //this.requestData({
            //    name: "getTabs"
            //});

            ////load specs for test
            //this.requestData({
            //    name: "getTestSpecs"
            //});

        },

        updateReadme: function(html) {
            var $readme = $(".gui-body-readme").html(html);
            $readme.find("a").attr("target", "_blank");
        },

        updateTestSpecs: function(specs) {
            if (!specs) {
                console.log("Not found specs");
                return;
            }
            this.specs = specs;
            this.updateComponentSpec();
        },

        updateComponentSpec: function() {
            var list = [];
            var name = this.getSelectedNames();
            if (this.specs) {
                var files = this.specs[name];
                if (files && files.length > 1) {
                    list = [""];
                    files.forEach(function(file) {
                        list.push(file);
                    });
                }
            }
            this.specComBox.setList(list);
            if (list.length) {
                this.specComBox.enable();
            } else {
                this.specComBox.disable();
            }
        },

        updateSelected: function() {
            var selectedItems = this.grid.getSelectedRows();
            var list = [];
            selectedItems.forEach(function(item) {
                list.push(item.name);
            });
            this.updateSelectedNames(list.join(","));
            this.updateComponentSpec();
            this.updateButtons();
        },

        updateButtons: function() {
            this.updateBuildButton();
            this.updateTestButton();
            this.updateLintButton();
            this.updatePreCommitButton();
            this.updatePreviewButton();
            this.updateListButton();
        },

        updateBuildButton: function() {
            var minifyChecked = $(".cb-minify").get(0).checked;
            var minify = "";
            if (minifyChecked) {
                minify = " -m";
            }

            var bundleChecked = $(".cb-bundle").get(0).checked;
            var bundle = "";
            if (bundleChecked) {
                bundle = " -b";
            }

            var buildCmd = "bd build " + this.getSelectedNames() + minify + bundle;
            $(".bd-build").attr("tooltip", buildCmd);
            var buildAllCmd = "bd build" + minify + bundle;
            $(".bd-build-all").attr("tooltip", buildAllCmd);
        },

        updateTestButton: function() {
            var spec = this.specComBox.getValue();
            if (spec) {
                spec = " -s " + spec;
            } else {
                spec = "";
            }

            var debugChecked = $(".cb-debug").get(0).checked;
            var debug = "";
            if (debugChecked) {
                debug = " -d";
            }

            var openChecked = $(".cb-open").get(0).checked;
            var open = "";
            if (openChecked) {
                open = " -o";
            }

            var testCmd = "bd test " + this.getSelectedNames() + spec + debug + open;
            $(".bd-test").attr("tooltip", testCmd);

            var testAllCmd = "bd test" + debug + open;
            $(".bd-test-all").attr("tooltip", testAllCmd);

        },

        updateLintButton: function() {

            var eslint = this.lintEslintComBox.getValue();
            if (eslint) {
                eslint = " -e " + eslint;
            } else {
                eslint = "";
            }

            var stylelintChecked = $(".cb-stylelint").get(0).checked;
            var stylelint = "";
            if (stylelintChecked) {
                stylelint = " -s";
            }

            var namingChecked = $(".cb-naming").get(0).checked;
            var naming = "";
            if (namingChecked) {
                naming = " -n";
            }

            var lintCmd = "bd lint " + this.getSelectedNames() + eslint + stylelint + naming;
            $(".bd-lint").attr("tooltip", lintCmd);
            var lintAllCmd = "bd lint" + eslint + stylelint + naming;
            $(".bd-lint-all").attr("tooltip", lintAllCmd);
        },

        updatePreCommitButton: function() {

            var quickChecked = $(".cb-quick").get(0).checked;
            var quick = "";
            if (quickChecked) {
                quick = " -q";
            }

            var minifyChecked = $(".cb-minify").get(0).checked;
            var minify = "";
            if (minifyChecked) {
                minify = " -m";
            }

            var preCommitCmd = "bd precommit " + this.getSelectedNames() + quick + minify;
            $(".bd-precommit").attr("tooltip", preCommitCmd);

            var preCommitAllCmd = "bd precommit" + quick + minify;
            $(".bd-precommit-all").attr("tooltip", preCommitAllCmd);
        },

        updatePreviewButton: function() {
            var env = this.envComBox.getValue();
            if (env) {
                env = " -e " + env;
            }
            var previewCmd = "bd preview " + this.getSelectedNames() + env;
            $(".bd-preview").attr("tooltip", previewCmd);
        },

        updateListButton: function() {
            var sort = this.sortComBox.getValue();
            if (sort) {
                sort = " -s " + sort;
            }
            var listCmd = "bd list " + this.getSelectedNames() + sort;
            $(".bd-list").attr("tooltip", listCmd);
            var listAllCmd = "bd list" + sort;
            $(".bd-list-all").attr("tooltip", listAllCmd);
        },

        initGrid: function() {
            var self = this;
            var container = document.querySelector(".gui-component-list");
            this.grid = new Grid(container);
            this.grid.bind("onClick", function(e, d) {
                this.unselectAll();
                this.setSelectedRow(d.row, d.e);
                self.updateSelected();
            });
            this.grid.bind("onSelectedChanged", function(e, d) {
                self.updateSelected();
            }).bind("onDblClick", function(e, d) {
                //click build button
                setTimeout(function() {
                    $(".bd-build").click();
                }, 100);
            }).bind("onContextMenu", function(e, d) {
                self.showContextMenu(d);
            });
            this.grid.setOption({
                rowHeight: 35,
                showHeader: false,
                showCheckbox: true,
                treeFormat: function(v, rowItem) {
                    var className = "";
                    var style = self.getCookie("style-" + rowItem.name);
                    if (style) {
                        className = "gui-style-" + style;
                    }
                    return '<div class="' + className + '">' + v + "</div>";
                },
                rowFilter: function(rowData) {
                    var keywords = self.keywords;
                    if (!keywords) {
                        return true;
                    }
                    var name = rowData.name;
                    name = name.toLowerCase();
                    var arr = keywords.split(" ");
                    for (var i = 0, l = arr.length; i < l; i++) {
                        var item = arr[i];
                        if (item && name.indexOf(item) !== -1) {
                            return true;
                        }
                    }
                    return false;
                }
            });
            this.grid.showLoading();
        },

        resize: function() {
            if (this.currentTab && this.currentTab !== "project") {
                return;
            }
            this.grid.resize();
        },

        getContentMenuList: function(name) {
            var html = "";
            html += '<div class="gui-context-head">Set Name Style</div>';
            var styleList = ["", "star", "heart", "check", "error", "question"];
            styleList.forEach(function(item) {
                var className = "gui-style-" + item;
                if (!item) {
                    className = "";
                }
                html += '<div class="gui-context-item" value="' + item + '">';
                html += '<div class="' + className + '">' + name + "</div>";
                html += "</div>";
            });
            return html;
        },

        showContextMenu: function(d) {

            var e = d.e;
            e.preventDefault();

            this.contextMenuRemoveHandler();

            this.grid.unselectAll();
            this.grid.setSelectedRow(d.row, d.e);
            this.updateSelected();

            var rowItem = this.grid.getRowItem(d.row);
            var name = rowItem.name;
            var html = this.getContentMenuList(name);
            //var content = d.cellNode.innerText;

            var contextMenu = document.createElement("div");
            contextMenu.className = "gui-context-menu";
            contextMenu.innerHTML = html;
            document.body.appendChild(contextMenu);

            var self = this;

            $(contextMenu).bind("contextmenu", function() {
                return false;
            }).delegate(".gui-context-item", "click", function(e) {
                var elem = $(this);
                var value = elem.attr("value");
                //console.log(name, value);
                if (value) {
                    self.setCookie("style-" + name, value);
                } else {
                    self.delCookie("style-" + name);
                }
                self.contextMenuRemoveHandler();
                self.updateGridRow(rowItem);
            });

            var contextMenuLayout = function(contextMenu, e) {
                var top = e.pageY;
                var ch = contextMenu.clientHeight;
                if (top + ch > document.body.clientHeight) {
                    top = top - ch;
                }
                var left = e.pageX;
                contextMenu.style.top = top + "px";
                contextMenu.style.left = left + "px";
            };

            contextMenuLayout(contextMenu, e);

            var contextMenuCloseHandler = function(e) {
                if (!self.contextMenu) {
                    return;
                }
                if (self.contextMenu === e.target || self.contextMenu.contains(e.target)) {
                    return;
                }
                document.removeEventListener("click", contextMenuCloseHandler);
                self.contextMenuRemoveHandler();
            };

            document.addEventListener("click", contextMenuCloseHandler);

            this.contextMenu = contextMenu;

        },

        contextMenuRemoveHandler: function() {
            if (this.contextMenu) {
                this.contextMenu.parentNode.removeChild(this.contextMenu);
                this.contextMenu = null;
            }
        },

        updateGridRow: function(rowItem) {
            this.grid.invalidateRow(rowItem.tg_index);
            this.grid.render();
        },

        getSelectedNames: function() {
            return this.getCookie("names") || "";
        },

        updateSelectedNames: function(names) {
            this.setCookie("names", names);
            $(".gui-selected-names").text(names);
        },

        showList: function(list) {

            this.list = list;

            var names = this.getSelectedNames();
            var previousNames = names.split(",");
            var rows = [];
            list.forEach(function(item) {
                var row = {
                    name: item
                };
                if (Util.inList(item, previousNames)) {
                    row.selected = true;
                }
                rows.push(row);
            });

            var gridData = {
                columns: [{
                    id: "name",
                    sortable: false,
                    cellClass: "gui-grid-name",
                    name: "Components",
                    width: 275
                }],
                rows: rows
            };

            var self = this;

            this.grid.onceRendered(function() {

                this.hideLoading();

                var selected = this.getSelectedRow();
                if (selected) {
                    this.scrollRowIntoView(selected.tg_index);
                    self.updateSelectedNames(selected);
                }

                //update button all cmd
                self.updateSelected();

                self.updateProjectRelated();

            });
            console.log(gridData);
            this.grid.setData(gridData);
            this.grid.render();

        },

        //===================================================================================
        /* eslint-disable max-statements,complexity */
        bindEvents: function() {

            this.terminalComBoxHandler();
            this.specComBoxHandler();
            this.lintEslintComBoxHandler();
            this.sortComBoxHandler();
            this.envComBoxHandler();

            var self = this;

            $("button").unbind().bind("click", function(e) {
                var button = $(this);
                var handler = {
                    "bt-version": function(button) {
                        self.versionHandler(button);
                    },
                    "bt-version-reset": function(button) {
                        $(".it-version").val(self.info.version);
                    },
                    "bt-version-major": function(button) {
                        self.versionPlusHandler(button, 0);
                    },
                    "bt-version-minor": function(button) {
                        self.versionPlusHandler(button, 1);
                    },
                    "bt-version-patch": function(button) {
                        self.versionPlusHandler(button, 2);
                    },
                    "bt-clear": function(button) {
                        self.$content.empty();
                    },
                    "bt-load": function(button) {
                        self.loadFileHandler();
                    },
                    "bt-locate": function(button) {
                        self.locateErrorHandler(button);
                    },
                    "bt-sync-file": function() {
                        self.syncFileHandler();
                    }
                };

                for (var id in handler) {
                    if (button.hasClass(id)) {
                        handler[id].call(this, button);
                        return;
                    }
                }
                self.buttonClickHandler(button);
            });

            $(".cb-autoscroll").unbind().change(function() {
                self.autoScrollHandler();
            });
            this.autoScrollHandler();

            $(".cb-minify").unbind().bind("change", function() {
                self.updateBuildButton();
                self.updatePreCommitButton();
            });

            $(".cb-bundle").unbind().bind("change", function() {
                self.updateBuildButton();
            });

            $(".cb-debug").unbind().bind("change", function() {
                self.updateTestButton();
            });

            $(".cb-open").unbind().bind("change", function() {
                self.updateTestButton();
            });

            $(".cb-stylelint").unbind().bind("change", function() {
                self.setCookie("lint_stylelint", this.checked);
                self.updateLintButton();
            });
            var stylelint = this.getCookie("lint_stylelint");
            if (stylelint === "true") {
                $(".cb-stylelint").attr("checked", "checked");
                self.updateLintButton();
            }

            $(".cb-naming").unbind().bind("change", function() {
                self.setCookie("lint_naming", this.checked);
                self.updateLintButton();
            });
            var naming = this.getCookie("lint_naming");
            if (naming === "true") {
                $(".cb-naming").attr("checked", "checked");
                self.updateLintButton();
            }

            $(".cb-quick").unbind().bind("change", function() {
                self.updatePreCommitButton();
            });

            this.keywords = this.getCookie("keywords");
            $(".gui-component-filter").unbind().bind("focus", function() {
                $(this).select();
            }).bind("change blur keyup", function() {
                self.keywordsHandler($(this).val());
            }).val(this.keywords);
            this.filterHandler(this.keywords);

            $(".it-message").unbind().bind("keyup", function(e) {
                if (e.keyCode === 13) {
                    self.sendMessageHandler($(this).val());
                    $(this).val("");
                }
            });

            var syncPath = this.getCookie("sync_path");
            if (syncPath) {
                $(".it-sync-path").val(syncPath);
            }

            return this;
        },
        /* eslint-enable */

        //===================================================================================

        terminalComBoxHandler: function() {

            if (!this.terminalComBox) {
                var self = this;
                this.terminalComBox = new ComBox($(".gui-terminal"));
                this.terminalComBox.onSelect = function(item) {
                    var value = item.value;
                    self.sendCmdHandler(value);
                    self.addHistoryItem("suggestion", value, 5);
                    self.terminalComBoxHandler();
                };
                this.terminalComBox.onEnter = function(value) {
                    self.sendCmdHandler(value);
                    self.addHistoryItem("suggestion", value, 5);
                    self.terminalComBoxHandler();
                };
                this.terminalComBox.onDelete = function(item) {
                    self.delHistoryItem("suggestion", item.value);
                };
            }

            var list = this.getHistoryList("suggestion");

            var itemList = [];
            list.forEach(function(item) {
                itemList.push({
                    name: item,
                    value: item,
                    delete: true
                });
            });

            var defaultList = Util.defaultSuggestion.cmd;
            if (defaultList) {
                defaultList.forEach(function(item) {
                    if (Util.inList(item, list)) {
                        return;
                    }
                    itemList.push({
                        name: item,
                        value: item
                    });
                });
            }

            this.terminalComBox.setList(itemList);
        },

        specComBoxHandler: function() {
            if (!this.specComBox) {
                var self = this;
                this.specComBox = new ComBox($(".gui-spec-combox"));
                this.specComBox.onChange = function(item) {
                    self.updateTestButton();
                };
            }

        },

        lintEslintComBoxHandler: function() {

            if (!this.lintEslintComBox) {
                var self = this;
                this.lintEslintComBox = new ComBox($(".gui-lint-eslint-combox"));
                this.lintEslintComBox.onChange = function(item) {
                    self.setCookie("lint_eslint", item.value);
                    self.updateLintButton();
                };
                this.lintEslintComBox.setList([
                    "",
                    "vue",
                    "ie11",
                    "sonar",
                    "es2019",
                    "es2018",
                    "es2017",
                    "es2016"
                ]);
            }

            var eslint = this.getCookie("lint_eslint");
            if (eslint) {
                this.lintEslintComBox.setValue(eslint);
                this.updateLintButton();
            }

        },

        sortComBoxHandler: function() {

            if (!this.sortComBox) {
                var self = this;
                this.sortComBox = new ComBox($(".gui-sort-combox"));
                this.sortComBox.onChange = function(item) {
                    self.setCookie("list_sort", item.value);
                    self.updateListButton();
                };
                this.sortComBox.setList([
                    "",
                    "mSize",
                    "dSize",
                    "mFiles",
                    "dFiles",
                    "name"
                ]);
            }

            var sort = this.getCookie("list_sort");
            if (sort) {
                this.sortComBox.setValue(sort);
                this.updateListButton();
            }

        },

        envComBoxHandler: function() {

            if (!this.envComBox) {
                var self = this;
                this.envComBox = new ComBox($(".gui-env-combox"));
                this.envComBox.onChange = function(item) {
                    self.setCookie("preview_env", item.value);
                    self.updatePreviewButton();
                };
                this.envComBox.setList([
                    "",
                    "DEV",
                    "QA",
                    "STG",
                    "LOCAL"
                ]);
            }

            var env = this.getCookie("preview_env");
            if (env) {
                this.envComBox.setValue(env);
                this.updateListButton();
            }

        },

        projectHistoryHandler: function() {

            if (!this.projectComBox) {
                var self = this;
                this.projectComBox = new ComBox($(".gui-project-combox"));
                this.projectComBox.onChange = function(item) {
                    var project = item.value;
                    if (self.info.project === project) {
                        return;
                    }
                    self.requestData({
                        name: "switchProject",
                        data: project
                    });
                };
                this.projectComBox.onDelete = function(item) {
                    var str = item.value + "=" + item.name;
                    self.delHistoryItem("project", str);
                    self.projectHistoryHandler();
                };
            }

            var currentProject = this.info.project;
            var list = [];
            var plist = this.getHistoryList("project");
            var projects = {};
            plist.forEach(function(item) {
                if (!item) {
                    return;
                }
                var arr = item.split("=");
                var value = arr[0];
                var name = arr[1];
                if (projects[value]) {
                    return;
                }
                projects[value] = true;

                var selected = false;
                if (currentProject === value) {
                    selected = true;
                }

                list.push({
                    selected: selected,
                    delete: true,
                    name: name,
                    value: value
                });

            });

            this.projectComBox.setList(list);

        },

        versionHandler: function(button) {

            var $version = $(".it-version");
            var version = $version.val();
            if (!version) {
                $version.focus();
                return;
            }

            this.info.version = version;

            this.requestData({
                name: "updateVersion",
                data: version
            });

        },

        versionPlusHandler: function(button, index) {
            var $version = $(".it-version");
            var version = $version.val();
            var arr = version.split(".");
            arr[index] = (parseInt(arr[index], 10) || 0) + 1;
            arr.length = index + 1;
            var list = [];
            for (var i = 0; i < 3; i++) {
                list.push(Util.toNum(arr[i]));
            }
            $version.val(list.join("."));
        },

        loadFileHandler: function() {
            var self = this;
            $(".it-load-file").unbind().bind("change", function() {
                self.showMessage("Loading file ...");
                var file = this.files[0];
                self.requestData({
                    name: "loadFile",
                    data: file
                });
            }).click();
        },

        getLocateList: function() {

            //find all c31
            var c31s = this.$content.find(".c31");

            var locateList = [];

            var pos = {};
            //remove same row
            c31s.each(function() {
                var c31 = $(this);
                var top = c31.offset().top;
                if (pos[top]) {
                    return;
                }
                pos[top] = c31;
                locateList.push(c31);
            });

            return locateList;

        },

        locateErrorHandler: function(button) {

            var locateList = this.getLocateList();
            if (!locateList.length) {
                var name = button.attr("name");
                button.html(name + " (Nothing)");
                setTimeout(function() {
                    button.html(name);
                }, 1000);
                return;
            }

            var locateFocus = null;
            var locatePrev = null;
            locateList.forEach(function(item) {
                if (locateFocus) {
                    return;
                }
                if (locatePrev) {
                    locateFocus = item;
                }
                if (item.hasClass("gui-locate-focus")) {
                    locatePrev = item;
                    item.removeClass("gui-locate-focus");
                }
            });
            if (!locateFocus) {
                locateFocus = locateList[0];
            }

            locateFocus.addClass("gui-locate-focus");

            var $output = $(".gui-output");

            var height = locateFocus.offset().top + $output.scrollTop();

            $output.stop().scrollTop(height);

        },

        syncFileHandler: function() {
            var syncPath = $(".it-sync-path").val();
            if (!syncPath) {
                //remove saved to use default
                this.setCookie("sync_path", "");
                this.showError("Please set sync path first");
                $(".it-sync-path").focus();
                return;
            }

            this.setCookie("sync_path", syncPath);

            //get selected components

            var selectedRows = this.grid.getSelectedRows();

            var componentList = [];
            selectedRows.forEach(function(item) {
                componentList.push(item.name);
            });

            if (!componentList.length) {
                this.showError("Please select a component");
                return;
            }

            this.showMessage("Selected sync components: " + componentList.join(", "));

            this.requestData({
                name: "syncFile",
                data: {
                    syncPath: syncPath,
                    componentList: componentList
                }
            });

        },

        //===================================================================================

        buttonClickHandler: function(button) {
            var cmd = button.attr("cmd") || button.attr("tooltip");
            if (!cmd) {
                console.log("Invalid button cmd");
                return this;
            }

            if (button.hasClass("gui-button-loading")) {
                return this;
            }
            button.addClass("gui-button-loading").attr("loading-cmd", cmd);
            this.showTitleStatus("> " + cmd);

            this.sendCmdHandler(cmd);
        },

        autoScrollHandler: function() {
            this.autoscroll = !!$(".cb-autoscroll:checked").length;
        },

        //===================================================================================

        keywordsHandler: function(keywords) {
            var self = this;
            clearTimeout(this.time_filter);
            this.time_filter = setTimeout(function() {
                self.filterHandler(keywords);
            }, 100);
        },

        filterHandler: function(keywords) {
            keywords = $.trim(keywords);
            if (keywords === this.keywords) {
                return;
            }
            this.keywords = keywords;
            this.setCookie("keywords", keywords);
            if (this.grid) {
                this.grid.update();
            }
            return this;
        },

        sendCmdHandler: function(cmd) {
            if (!cmd) {
                return;
            }

            this.showMessage(cmd, {
                color: "#ffffff",
                padding: "8px 8px",
                margin: "0px -5px",
                background: "#272822"
            });

            this.sendCmd(cmd);
        },

        sendMessageHandler: function(message) {
            if (!message) {
                return;
            }

            //console.log(message);
            var div = document.createElement("div");
            div.innerText = message;
            var str = div.innerHTML;
            this.sendMessage(str);
        },

        //===================================================================================

        messageHandler: function(data) {
            var str = data.data + "";
            if (!str) {
                return;
            }
            this.showMessage(str);
        },

        cmdInfoHandler: function(cmd, code) {
            //with error
            if (code && code !== 0) {
                this.showError("Complete: " + cmd + " (with error " + code + ")");
                return;
            }

            this.showMessage("Complete: " + cmd + " (without error " + code + ")", "#859900");

        },

        cmdHandler: function(data) {

            var cmd = data.cmd;
            var code = data.code;

            //when server force close will silent finish
            if (!data.silent) {
                this.cmdInfoHandler(cmd, code);
            }

            var button = $("button[loading-cmd='" + cmd + "']");
            if (!button.length) {
                return this;
            }

            //when server start run cmd
            if (data.start) {
                button.addClass("gui-button-loading").attr("loading-cmd", cmd);
                this.showTitleStatus("> " + cmd);
                this.showOutputLoading();
                return this;
            }

            //when server finish cmd
            button.removeClass("gui-button-loading").attr("loading-cmd", "");
            this.showTitleStatus();
            this.hideOutputLoading();

            return this;
        },

        showOutputLoading: function() {
            $(".gui-output-loading").show();
        },

        hideOutputLoading: function() {
            $(".gui-output-loading").hide();
        },

        //===================================================================================

        getHistoryList: function(type) {
            var list = [];
            var sugs = this.getCookie("history_" + type, true);
            if (sugs) {
                list = sugs.split("{|}");
            }
            return list;
        },

        addHistoryItem: function(type, value, max) {
            var newList = [value];
            var list = this.getHistoryList(type);
            list.forEach(function(item) {
                if (item !== value) {
                    newList.push(item);
                }
            });
            if (max) {
                if (newList.length > max) {
                    newList.length = max;
                }
            }
            var str = newList.join("{|}");
            this.setCookie("history_" + type, str, true);
        },

        delHistoryItem: function(type, value) {
            var newList = [];
            var list = this.getHistoryList(type);
            list.forEach(function(item) {
                if (item !== value) {
                    newList.push(item);
                }
            });
            var str = newList.join("{|}");
            this.setCookie("history_" + type, str, true);
        },

        //===================================================================================

        logAppendHandler: function(str, style) {
            if (!str) {
                return;
            }
            var div = $("<div/>").addClass("gui-output-line").html(str);
            div = this.logStyleHandler(div, style);

            this.$content.append(div);
            this.scrollTo();

            //debounce clean
            clearTimeout(this.timeout_clean);
            var self = this;
            this.timeout_clean = setTimeout(function() {
                self.logCleanHandler();
            }, 1000);

        },

        logStyleHandler: function(div, style) {
            if (!style) {
                return div;
            }
            if (typeof(style) === "object") {
                div.css(style);
            } else if (typeof(style) === "string") {
                div.css({
                    color: style
                });
            }
            return div;
        },

        logCleanHandler: function() {
            if (!this.info) {
                return;
            }
            var max = this.info.guiHistory || 2000;
            var l = this.$content.children().length;
            if (l < max) {
                return this;
            }
            var n = l - max;
            for (var i = 0; i < n; i++) {
                this.$content.children().first().remove();
            }
            return this;
        },

        scrollTo: function(now) {
            if (!this.autoscroll) {
                return;
            }

            var height = this.$content.outerHeight();
            var $output = $(".gui-output");

            if (now) {
                $output.stop().scrollTop(height);
                return;
            }

            if (this.scrolling) {
                $output.stop().scrollTop(this.scrollPos);
            }

            this.scrolling = true;
            this.scrollPos = height;

            var self = this;
            $output.stop().animate({
                scrollTop: height
            }, function() {
                self.scrolling = false;
            });
        },

        //===================================================================================

        showMessage: function(message, style) {
            this.logAppendHandler(message, style);
        },

        showError: function(message) {
            this.showMessage(message, "#ff0000");
        },

        showStatus: function(message) {
            this.showMessage(message, "#999999");
        },

        showTitleStatus: function(cmd) {
            if (cmd && cmd !== this.title) {
                document.title = cmd;
                this.showFavicon();
                return;
            }
            document.title = this.title;
            this.stopFavicon();
        },

        showFavicon: function() {
            var list = [
                [0, 0],
                [6, 0],
                [12, 0],
                [12, 6],
                [12, 12],
                [6, 12],
                [0, 12],
                [0, 6]
            ];
            this.ctx.clearRect(0, 0, 16, 16);
            var self = this;
            list.forEach(function(item, i) {
                if (i === self.faviconIndex) {
                    self.ctx.fillStyle = "#335d00";
                } else {
                    self.ctx.fillStyle = "#ee5d00";
                }
                self.ctx.fillRect(item[0], item[1], 4, 4);
            });
            this.faviconIndex += 1;
            if (this.faviconIndex > 7) {
                this.faviconIndex = 0;
            }
            this.favicon.href = self.canvas.toDataURL("image/png");

            clearTimeout(this.time_favicon);
            this.time_favicon = setTimeout(function() {
                self.showFavicon();
            }, 200);
        },

        stopFavicon: function() {
            clearTimeout(this.time_favicon);
            this.favicon.href = this.previousFavicon;
        },

        throttle: function(delay, callback) {
            clearTimeout(this.throttle_timeout);
            var now = new Date().getTime();
            var self = this;
            if (this.throttle_last && now < this.throttle_last + delay) {
                this.throttle_timeout = setTimeout(function() {
                    self.throttle_last = now + delay;
                    callback.apply(self);
                }, delay);
            } else {
                this.throttle_last = now;
                callback.apply(self);
            }
        },

        //===================================================================================

        getCookieKey: function(key, global) {
            var pre = "bd_";
            if (!global) {
                pre = "bd_" + this.info.name + "_";
            }
            key = pre + key;
            return key;
        },

        getCookie: function(key, global) {
            key = this.getCookieKey(key, global);
            if (window.localStorage) {
                return window.localStorage.getItem(key);
            }
            var strCookie = document.cookie || "";
            var list = strCookie.split(";");
            for (var i = 0, l = list.length; i < l; i++) {
                var item = list[i];
                var arr = item.split("=");
                var k = $.trim(arr[0]);
                if (k === key) {
                    return arr[1];
                }
            }
            return "";
        },

        setCookie: function(key, value, global) {
            key = this.getCookieKey(key, global);
            if (window.localStorage) {
                return window.localStorage.setItem(key, value);
            }
            var expireDays = 30;
            var date = new Date();
            date.setTime(date.getTime() + expireDays * 24 * 3600 * 1000);
            document.cookie = key + "=" + value + ";expires=" + date.toGMTString();
        },

        delCookie: function(key, global) {
            key = this.getCookieKey(key, global);
            if (window.localStorage) {
                return window.localStorage.removeItem(key);
            }
            var date = new Date();
            date.setTime(date.getTime() - 1);
            document.cookie = key + "=;expires=" + date.toGMTString();
        }
    };

    //=====================================================================================
    var main;
    window.addEventListener("load", function() {
        main = new Main();
    });
    window.addEventListener("resize", function() {
        if (main) {
            main.resize();
        }
    });

})(window);